package xyz.yamdestiny.logparser.util;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xyz.yamdestiny.logparser.entity.ALog;
import xyz.yamdestiny.logparser.entity.Entrance;
import xyz.yamdestiny.logparser.entity.Node;

public class SpecialOutput {

    private static final double NODE_TIME = new Double(50); // ms
    private static final double ENTRANCE_TIME = new Double(1000); // ms

    private final Logger logger = LoggerFactory.getLogger(SpecialOutput.class);

    public void printLog(List<ALog> logs) {
        Map<Entrance, List<Double>> entranceMap = new HashMap<>();
        Map<Node, List<Double>> nodeMap = new HashMap<>();
        for (ALog logFile : logs) {

            Entrance entrance = logFile.getEntrance();

            collectData(entranceMap, entrance);

            Entrance keyEntrance = getKeyNode(entranceMap, entrance);

            if (entrance.getTime() > keyEntrance.getTime() && entrance.getTime() >= ENTRANCE_TIME) {
                // set max time info
                keyEntrance.setFileName(logFile.getFileName());
                keyEntrance.setFilePath(logFile.getFilePath());
                keyEntrance.setParentFilePath(logFile.getParentFilePath());

                keyEntrance.setContent(logFile.getContent());
                keyEntrance.setTime(entrance.getTime());
            }

            for (Node node : logFile.getNodes()) {

                collectData(nodeMap, node);

                Node keyNode = getKeyNode(nodeMap, node);

                if (node.getTime() > keyNode.getTime() && node.getTime() >= NODE_TIME) {
                    // set max time info
                    keyNode.setFileName(logFile.getFileName());
                    keyNode.setFilePath(logFile.getFilePath());
                    keyNode.setParentFilePath(logFile.getParentFilePath());

                    keyNode.setContent(logFile.getContent());
                    keyNode.setTime(node.getTime());
                }
            }

            // for (Entrance entrace : logFile.getSubEntrance()) {
            // logger.info("Entraces: {}, time: {}", entrace.getName(), entrace.getTime());
            // }
        }

        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(2);

        for (Entry<Entrance, List<Double>> entry : entranceMap.entrySet()) {

            Entrance key = entry.getKey();
            List<Double> list = entry.getValue();

            double avgTime = list.stream().mapToDouble((x) -> x).average().getAsDouble();
            if (avgTime <= NODE_TIME) {
                continue;
            }

            double maxTime = list.stream().mapToDouble((x) -> x).max().getAsDouble();
            double minTime = list.stream().mapToDouble((x) -> x).min().getAsDouble();

            logger.info(
                    "\nEntrance too long, name: {}, time: [min={}ms,max={}ms,avg={}ms], count: {}, max time content: \n{}",
                    key, nf.format(minTime), nf.format(maxTime), nf.format(avgTime), list.size(), key.getContent());
            logger.info("****************************");
        }

        for (Entry<Node, List<Double>> entry : nodeMap.entrySet()) {

            Node key = entry.getKey();
            List<Double> list = entry.getValue();

            double maxTime = list.stream().mapToDouble((x) -> x).max().getAsDouble();
            if (maxTime <= NODE_TIME) {
                continue;
            }
            double minTime = list.stream().mapToDouble((x) -> x).min().getAsDouble();
            double avgTime = list.stream().mapToDouble((x) -> x).average().getAsDouble();

            logger.info(
                    "\nNode too long, name: {}, time: [min={}ms,max={}ms,avg={}ms], count: {}, max time content: \n{}",
                    key, nf.format(minTime), nf.format(maxTime), nf.format(avgTime), list.size(), "");
            logger.info("----------------------------");
        }
    }

    private <T extends Node> T getKeyNode(Map<T, List<Double>> map, T node) {
        T keyEntrance = null;

        for (T temp : map.keySet()) {
            if (temp.equals(node)) {
                keyEntrance = temp;
                break;
            }
        }
        if (keyEntrance == null) {
            throw new IllegalArgumentException("error key");
        }
        return keyEntrance;
    }

    private <T extends Node> void collectData(Map<T, List<Double>> map, T node) {
        List<Double> list = map.get(node);
        if (list == null || list.size() <= 0) {
            list = new ArrayList<>();
            map.put(node, list);
        }

        list.add(node.getTime());
    }

}
